#include "ksix.h"

#include "sixplayer.h"
#include "asyncplayer.h"
#include "misc.h"
#include "config.h"

//#include <unistd.h>

#include <cassert>
#include <fstream>

#include <qpainter.h>
//#include <qpaintdevicemetrics.h>
#include <qbuttongroup.h>
//#include <qhbox.h>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QThread>
#include <QTextEdit>
#include <QDialog>
#include <QFileDialog>
#include <QApplication>
#include <QMenu>
#include <QMenuBar>
#include <QToolBar>

#include <QInputDialog>
#include <QPixmap>
#include <QStatusBar>

#include <sstream>
#include <fstream>

using std::endl;
using std::istringstream;
using std::ostringstream;
using std::ifstream;
using std::ofstream;

QString KSix::secToQString(int sec)
{
  int h = sec / 3600;
  int m = (sec % 3600) / 60;
  int s = (sec % 60);
  QString r;
  r.sprintf("%d:%.2d:%.2d", h, m, s);
  return r;
}

QString KSix::moveToString(const HexMove &m, int n, const HexBoard &b)
{
  assert(!m.isNull());
  QString r;
  if(m.isSwap()) {
    r = tr("%1. Swap").arg(n);
  } else if(m.isResign()) {
    r = tr("%1. Resign").arg(n);
  } else if(m.isForfeit()) {
    r = tr("%1. Forfeit").arg(n);
  } else {
    int xc, yc;
    if(isTransverted() && (n > 2))
      b.field2Coords(m.field(), &yc, &xc);
    else
      b.field2Coords(m.field(), &xc, &yc);
    r = tr("%1. %2%3").arg(n).arg(QChar('A' + xc)).arg(yc + 1);
  }
  return r;
}

QString KSix::playerToString(PlayerType p)
{
  switch(p) {
  case PLAYER_HUMAN:
    return tr("human");
  case PLAYER_BEGINNER:
    return tr("beginner");
  case PLAYER_INTERMEDIATE:
    return tr("intermediate");
  case PLAYER_ADVANCED:
    return tr("advanced");
  case PLAYER_EXPERT:
    return tr("expert");
  default:
    return QString::null;
  }
}

KSix::PlayerType KSix::stringToPlayer(const QString &s)
{
  if(s == /*tr*/("human")) {
    return PLAYER_HUMAN;
  }
  if(s == /*tr*/("beginner")) {
    return PLAYER_BEGINNER;
  }
  if(s == /*tr*/("intermediate")) {
    return PLAYER_INTERMEDIATE;
  }
  if(s == /*tr*/("advanced")) {
    return PLAYER_ADVANCED;
  }
  if(s == /*tr*/("expert")) {
    return PLAYER_EXPERT;
  }
  return PLAYER_NONE;
}

HexPlayer *KSix::createPlayer(PlayerType p)
{
  bool allowResign = _toggleResignAction->isChecked();
  switch(p) {
  case PLAYER_HUMAN:
    return new AsyncPlayer();
  case PLAYER_BEGINNER:
    return new SixPlayer(SixPlayer::BEGINNER, allowResign, this);
  case PLAYER_INTERMEDIATE:
    return new SixPlayer(SixPlayer::INTERMEDIATE, allowResign, this);
  case PLAYER_ADVANCED:
    return new SixPlayer(SixPlayer::ADVANCED, allowResign, this);
  case PLAYER_EXPERT:
    return new SixPlayer(SixPlayer::EXPERT, allowResign, this);
  default:
    assert(0);
  }
  // suppress warning
  return 0;
}

KSix::KSix()
    : QMainWindow(0/*, "six"*/),
      _hexWidget(new KHexWidget(this))//,
    //  _printer(0)
{
  _blackPlayer = PLAYER_NONE;
  _whitePlayer = PLAYER_NONE;

  _killingBlackPlayer = false;
  _killingWhitePlayer = false;
  _stoppingMatch = false;
  _thinking = HEX_MARK_EMPTY;

  // tell the KMainWindow that this is indeed the main widget
  QWidget* centerWidget = new QWidget(this);
  QVBoxLayout* mainLayout = new QVBoxLayout(centerWidget);
  mainLayout->setContentsMargins(0, 0, 0, 0);
  mainLayout->setSpacing(0);
  _helpContentLabel = new QLabel(centerWidget);
  _helpContentLabel->setVisible(false);
  _helpContentLabel->setWordWrap(true);
#if  defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
  QVBoxLayout* topLayout = new QVBoxLayout();
#else
  QHBoxLayout* topLayout = new QHBoxLayout();
#endif
  topLayout->setContentsMargins(0, 0, 0, 0);
  topLayout->setSpacing(0);
  topLayout->addWidget(_helpContentLabel);
  _helpImageLabel = new QLabel(centerWidget);
  _helpImageLabel->setVisible(false);
  topLayout->addWidget(_helpImageLabel);

  mainLayout->addLayout(topLayout);
  mainLayout->addWidget(_hexWidget);
  _hexWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  centerWidget->setLayout(mainLayout);
  setCentralWidget(centerWidget);

  // then, setup our actions
  setupActions();

  // and a status bar
  statusBar()->show();
  _messageLabel = new ClickableLabel(statusBar());
  _messageLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
  statusBar()->addWidget(_messageLabel, 1/*, 1*/);
  connect(_messageLabel, SIGNAL(released()), this, SLOT(flash()));

  // add a frame containing black's clock
  {
    QWidget *hbox= new QWidget(statusBar()/*, 0*/);
	QHBoxLayout* hLayout = new QHBoxLayout();
	hbox->setLayout(hLayout);
    _blackClockMark = new QLabel(hbox/*, 0*/);
    _blackClockMark->setPixmap(/*BarIcon*/QPixmap(":/lo16-action-blackplayer.png"));
    _blackClockLabel = new QLabel(hbox);
	hLayout->addWidget(_blackClockMark);
	hLayout->addWidget(_blackClockLabel);
    statusBar()->addWidget(hbox/*, 0, 1*/);
  }
  // add a frame containing white's clock
  {
    QWidget *hbox= new QWidget(statusBar()/*, 0*/);
	QHBoxLayout* hLayout = new QHBoxLayout();
	hbox->setLayout(hLayout);
    _whiteClockMark = new QLabel(hbox, 0);
    _whiteClockMark->setPixmap(QPixmap(":/lo16-action-whiteplayer.png"));
    _whiteClockLabel = new QLabel(hbox);
	hLayout->addWidget(_whiteClockMark);
	hLayout->addWidget(_whiteClockLabel);
    statusBar()->addWidget(hbox/*, 0, 1*/);
  }

  connect(_hexWidget, SIGNAL(signalClickLeft(HexField)),
          this, SLOT(clickLeft(HexField)));

  readOptions();
}

KSix::~KSix()
{
}

void KSix::setupActions()
{
	QMenuBar* mainMenu = this->menuBar();
	mainMenu->setNativeMenuBar(false);
	QMenu* gameMenu = mainMenu->addMenu(tr("File"));
    _gameNewAction = gameMenu->addAction(tr("&New Game")/*, gameMenu*/);
	_gameNewAction->setIcon(QIcon(":/lo24-action-new.png"));
	connect(_gameNewAction, SIGNAL(triggered()), SLOT(gameNew()));

	QAction* gameOpenAction = gameMenu->addAction(tr("&Open Game"));
	connect(gameOpenAction, SIGNAL(triggered()), SLOT(fileSave()));
	
	QAction* gameSaveAction = gameMenu->addAction(tr("&Save Game"));
	connect(gameSaveAction, SIGNAL(triggered()), SLOT(fileSave()));

	QAction* gameSaveAsAction = gameMenu->addAction("&Save Game As");
	connect(gameSaveAsAction,SIGNAL(triggered()),SLOT(fileSaveAs()));

	QAction* importPBEMAction = gameMenu->addAction(tr("&Import PBEM Game"));
	connect(importPBEMAction, SIGNAL(triggered()), SLOT(fileImportPBEMGame()));

	QAction* printPositionAction = gameMenu->addAction(tr("&Print Position"));
	connect(printPositionAction, SIGNAL(triggered()), SLOT(filePrint()));

	QAction* quitAction = gameMenu->addAction(tr("quit"));
	quitAction->setIcon(QIcon(":/lo24-action-quit.png"));
	connect(quitAction,SIGNAL(triggered()),SLOT(close()));
    
	// game
	
	QMenu* playMenu = mainMenu->addMenu(tr("Game"));
	_gamePreviousMoveAction = playMenu->addAction(tr("&Previous Move"));
	_gamePreviousMoveAction->setIcon(QIcon(":/lo24-action-prev.png"));
	connect(_gamePreviousMoveAction, SIGNAL(triggered()), SLOT(gamePreviousMove()));

    _gameNextMoveAction = playMenu->addAction(tr("&Next Move"));
	_gameNextMoveAction->setIcon(QIcon(":/lo24-action-next.png"));
	connect(_gameNextMoveAction, SIGNAL(triggered()), SLOT(gameNextMove()));


    _gameFirstMoveAction = playMenu->addAction(tr("&First Move"));
	connect(_gameFirstMoveAction,SIGNAL(triggered()),SLOT(gameFirstMove()));


    _gameLastMoveAction = playMenu->addAction(tr("&Last Move"));
	connect(_gameLastMoveAction, SIGNAL(triggered()), SLOT(gameLastMove()));


    _gameSuspendAction = playMenu->addAction(tr("&Pause"));
	_gameSuspendAction->setIcon(QIcon(":/lo24-action-pause.png"));
	connect(_gameSuspendAction, SIGNAL(triggered()), SLOT(gameSuspend()));


	_gameResumeAction = playMenu->addAction(tr("&Continue"));
	_gameResumeAction->setIcon(QIcon(":/lo24-action-resume.png"));
	connect(_gameResumeAction, SIGNAL(triggered()), SLOT(gameResume()));


	_gameMoveAction = playMenu->addAction(tr("&Move"));
	connect(_gameMoveAction, SIGNAL(triggered()), SLOT(gameMove()));

	_gameSwapAction = playMenu->addAction(tr("&Swap"));
	_gameSwapAction->setIcon(QIcon(":/lo24-action-swap.png"));
	connect(_gameSwapAction, SIGNAL(triggered()), SLOT(gameSwap()));

	_gameResignAction = playMenu->addAction(tr("&Resign"));
	connect(_gameResignAction,SIGNAL(triggered()),SLOT(gameResign()));


//     // bars
//     _toolbarAction = KStdAction::showToolbar(this, SLOT(showToolbar()),
//                                              actionCollection());
//     _statusbarAction = KStdAction::showStatusbar(this, SLOT(showStatusbar()),
//                                                  actionCollection());
// 
    // swap allowed
	_toggleSwapAction = playMenu->addAction(tr("Swa&p Allowed"));
	_toggleSwapAction->setCheckable(true);
	connect(_toggleSwapAction, SIGNAL(triggered()), SLOT(toggleSwap()));


    // board size
    {
      QStringList itemList;
      _boardSizeAction = new KSelectAction(tr("Board Si&ze"),this/*, 0,actionCollection(), "boardSize"*/);
      itemList.append(tr("&4"));
      itemList.append(tr("&5"));
      itemList.append(tr("&6"));
      itemList.append(tr("&7"));
      itemList.append(tr("&8"));
      itemList.append(tr("&9"));
      itemList.append(tr("&10"));
      itemList.append(tr("&11"));
#ifndef OLYMPICS
      itemList.append(tr("&12"));
      itemList.append(tr("&13"));
      itemList.append(tr("&14"));
      itemList.append(tr("&15"));
#endif
      _boardSizeAction->setItems(itemList);
      connect(_boardSizeAction, SIGNAL(triggered(int)),
              this, SLOT(setBoardSizeByMenuItem(int)));
	  playMenu->addAction(_boardSizeAction);
    }

	QMenu* resignMenu = menuBar()->addMenu(tr("Settings"));
    // resign allowed
	_toggleResignAction = resignMenu->addAction(tr("Resign Allowed"));
	_toggleResignAction->setCheckable(true);
	connect(_toggleResignAction,SIGNAL(triggered()),SLOT(toggleResign()));


    // numbering
	_toggleNumberingAction = resignMenu->addAction(tr("Move &Numbering"));
	_toggleNumberingAction->setCheckable(true);
	connect(_toggleNumberingAction, SIGNAL(triggered()), SLOT(toggleNumbering()));

    // show thinking
	_showThinkingAction = resignMenu->addAction(tr("Show Th&inking"));
	_showThinkingAction->setCheckable(true);
	connect(_showThinkingAction, SIGNAL(triggered()),SLOT(toggleShowThinking()));

    // swap style
    {
      QStringList itemList;
      _swapStyleAction =
        new KSelectAction(tr("Swap St&yle"),this /*0,actionCollection(), "swapStyle"*/);
      itemList.append(tr("&Board"));
      itemList.append(tr("&Players"));
      _swapStyleAction->setItems(itemList);
      connect(_swapStyleAction, SIGNAL(triggered(int)),
              this, SLOT(setSwapStyleByMenuItem(int)));
	  resignMenu->addAction(_swapStyleAction);
    }

    // display mode
    {
      QStringList itemList;
      _displayModeAction =
        new KSelectAction(tr("&Display Mode"),this /*0,actionCollection(), "displayMode"*/);
      itemList.append(tr("&Diagonal"));
      itemList.append(tr("&Flat"));
      _displayModeAction->setItems(itemList);
      connect(_displayModeAction, SIGNAL(triggered(int)),
              this, SLOT(setDisplayModeByMenuItem(int)));
	  resignMenu->addAction(_displayModeAction);
    }

    // black player
    {
      QStringList itemList;
      _blackPlayerAction =
        new KSelectAction(tr("&Black Player"), this/*"blackplayer", 0, actionCollection(), "blackPlayer"*/);
      itemList.append(tr("&Human"));
      itemList.append(tr("&Beginner"));
      itemList.append(tr("&Intermediate"));
      itemList.append(tr("&Advanced"));
      itemList.append(tr("&Expert"));
      _blackPlayerAction->setItems(itemList);
      connect(_blackPlayerAction, SIGNAL(triggered(int)),
              this, SLOT(setBlackPlayerByMenuItem(int)));
	  resignMenu->addAction(_blackPlayerAction);
    }

    // white player
    {
      QStringList itemList;
      _whitePlayerAction =
        new KSelectAction(tr("&White Player"),this /*"whiteplayer", 0,actionCollection(), "whitePlayer"*/);
      itemList.append(tr("&Human"));
      itemList.append(tr("&Beginner"));
      itemList.append(tr("&Intermediate"));
      itemList.append(tr("&Advanced"));
      itemList.append(tr("&Expert"));
      _whitePlayerAction->setItems(itemList);
      connect(_whitePlayerAction, SIGNAL(triggered(int)),
              this, SLOT(setWhitePlayerByMenuItem(int)));
	  resignMenu->addAction(_whitePlayerAction);
    }

	QMenu* helpMenu = menuBar()->addMenu(tr("Help"));
	// resign allowed
	_showRuleAction = helpMenu->addAction(tr("Game Rule"));
	_showRuleAction->setCheckable(true);
	_showRuleAction->setIcon(QIcon(":/lo24-action-rule.png"));
	connect(_showRuleAction, SIGNAL(triggered()), SLOT(showRule()));

	_showAboutAction = helpMenu->addAction(tr("About Six"));
	_showAboutAction->setCheckable(true);
	_showAboutAction->setIcon(QIcon(":/lo24-action-about.png"));
	connect(_showAboutAction, SIGNAL(triggered()), SLOT(showAbout()));

	QAction* aboutQtAction = helpMenu->addAction(tr("About Qt"));
	connect(aboutQtAction, SIGNAL(triggered()),qApp, SLOT(aboutQt()));

	QToolBar* toolbar = this->addToolBar(tr("Game"));
	toolbar->addAction(_gameNewAction);
	toolbar->addAction(_gamePreviousMoveAction);
	toolbar->addAction(_gameNextMoveAction);
	toolbar->addAction(_gameSuspendAction);
	toolbar->addAction(_gameResumeAction);
	toolbar->addAction(_gameSwapAction);

	toolbar->addAction(_showRuleAction);

	toolbar->addAction(_showAboutAction);
// 
//     // configure
//     KStdAction::keyBindings(this, SLOT(configureKeys()), actionCollection());
//     KStdAction::configureToolbars(this, SLOT(configureToolbars()),
//                                   actionCollection());
//     KStdAction::saveOptions(this, SLOT(saveOptions()), actionCollection());

//    createGUI();
}

void KSix::saveStandardProperties(KConfig *config)
{
  // the 'config' object points to the session managed
  // config file.  anything you write here will be available
  // later when this app is restored
  config->beginGroup("General");
  config->setValue("version", "0.1");
  config->endGroup();
  config->beginGroup("Appearance");
  config->setValue("geometry", size());
  config->setValue("showToolBar", _toolbarAction->isChecked());
  config->setValue("showStatusBar", _statusbarAction->isChecked());
  config->endGroup();
  config->beginGroup("Game Options");
  config->setValue("swapAllowed", _toggleSwapAction->isChecked());
  config->setValue("boardSize", _boardSize);
  config->setValue("blackPlayer", playerToString(_blackPlayer));
  config->setValue("whitePlayer", playerToString(_whitePlayer));
  config->setValue("resignAllowed", _toggleResignAction->isChecked());
  config->endGroup();
  config->beginGroup("Display Options");
  config->setValue("moveNumbering", _toggleNumberingAction->isChecked());
  config->setValue("showThinking", _showThinkingAction->isChecked());
  if(_swapStyleAction->currentItem() == 0) {
    config->setValue("swapStyle", "board");
  } else {
    config->setValue("swapStyle", "players");
  }
  if(_displayModeAction->currentItem() == 0) {
    config->setValue("displayMode", "diagonal");
  } else {
    config->setValue("displayMode", "flat");
  }
  config->endGroup();
}

void KSix::saveGameProperties(KConfig *config)
{
  Poi<HexMatch> m = ((!_match.null()) ? _match : _oldMatch);
  assert(!m.null());
  ostringstream os;
  (*m).game().save(os);
  config->beginGroup("Game State");
  config->setValue("game", os.str().c_str());
  config->setValue("gameChanged", (*m).game().isChanged());
  config->setValue("gameBranched", (*m).game().isBranched());
  config->setValue("gameFilename", _filename);
  config->endGroup();
}

void KSix::saveProperties(KConfig *config)
{
  gameSuspend();
  saveStandardProperties(config);
  saveGameProperties(config);
  config->sync();
}

void KSix::readStandardProperties(KConfig *config)
{
  // the 'config' object points to the session managed
  // config file.  this function is automatically called whenever
  // the app is being restored.  read in here whatever you wrote
  // in 'saveProperties'
  config->beginGroup("Appearance");
  QSize defSize(600, 500);
  QSize size = config->value("geometry", /*&*/defSize).toSize();
  bool stb = config->value("showToolBar", true).toBool();
  bool ssb = config->value("showStatusBar", true).toBool();
  config->endGroup();

  config->beginGroup("Game Options");
  bool sa = config->value("swapAllowed", true).toBool();
  int bs = config->value("boardSize", 11).toInt();
  PlayerType bp = stringToPlayer(config->value("blackPlayer", "human").toString());
  PlayerType wp = stringToPlayer(config->value("whitePlayer", "beginner").toString());
  bool ra = config->value("resignAllowed", true).toBool();
  config->endGroup();

  config->beginGroup("Display Options");
  bool mn = config->value("moveNumbering", true).toBool();
  bool st = config->value("showThinking", false).toBool();
  QString dm = config->value("displayMode", "diagonal").toString();
  QString ss = config->value("swapStyle", "board").toString();

  resize(size);

//   if(_toolbarAction->isChecked() != stb)
//     _toolbarAction->activate();
//   if(_statusbarAction->isChecked() != ssb)
//     _statusbarAction->activate();

  setBlack(bp);
  setWhite(wp);

  assert(4 <= bs && bs <= 15);
  _boardSizeAction->setCurrentItem(bs - 4);
  _boardSize = bs;

//   if(_toggleSwapAction->isChecked() != sa)
//     _toggleSwapAction->activate();
//   if(_toggleResignAction->isChecked() != ra)
//     _toggleResignAction->activate();
  if(_match.null())
    newMatch();

//   if(_toggleNumberingAction->isChecked() != mn)
//     _toggleNumberingAction->activate();
//   if(_showThinkingAction->isChecked() != st)
//     _showThinkingAction->activate();

  if(dm == "diagonal") {
    _displayModeAction->setCurrentItem(0);
    setDisplayModeByMenuItem(0);
  } else {
    _displayModeAction->setCurrentItem(1);
    setDisplayModeByMenuItem(1);
  }

  if(ss == "board") {
    _swapStyleAction->setCurrentItem(0);
    setSwapStyleByMenuItem(0);
  } else {
    _swapStyleAction->setCurrentItem(1);
    setSwapStyleByMenuItem(1);
  }
}

void KSix::readGameProperties(KConfig *config)
{
  config->beginGroup("Game State");
  bool changed = config->value("gameChanged", false).toBool();
  bool branched = config->value("gameBranched", false).toBool();
  QString filename = config->value("gameFilename", "").toString();
  QString savedGame = config->value("game", "").toString();
  istringstream is((std::string)savedGame.toLatin1());
  HexGame g;
  g.load(is);
  g.setChanged(changed);
  g.setBranched(branched);
  setFilename(filename);
  if(is.fail()) {
    DBG << "Six could not restore session." << std::endl;
  } else {
    newMatch(g);
  }
}

void KSix::readProperties(KConfig *config)
{
  readStandardProperties(config);
  readGameProperties(config);
  //...
}

void KSix::saveOptions()
{
  KConfig config("six.ini",QSettings::IniFormat);
  saveStandardProperties(&config);
  //  saveProperties(config);
  config.sync();
}

void KSix::readOptions()
{
  //KConfig *config = KGlobal::config();
	KConfig config("six.ini", QSettings::IniFormat);
    readStandardProperties(&config);
    //readProperties(config);
}

bool KSix::verifyUnsavedGame(const QString &question)
{
  return (!(*_match).game().isChanged() ||
          QMessageBox::warning(this,tr("warning"), question,QMessageBox::Yes|QMessageBox::No)
          == QMessageBox::Yes);
//	return true;
}

bool KSix::loadGame(const QString &filename)
{
  ifstream is(filename.toLatin1());
  HexGame g;
  g.load(is);
  if(!is.fail() && 4 <= g.board().xs() && g.board().xs() <= 15 &&
     g.board().xs() == g.board().ys()) {
    setFilename(filename);
    // sync actions with reality
    int bs = g.board().xs();
    _boardSizeAction->setCurrentItem(bs - 4);
    _boardSize = bs;
    _toggleSwapAction->setChecked(g.initialState().swappable());
    // create new match
    newMatch(g);
    return true;
  } else {
		QMessageBox::critical(this,tr("error"), tr("Loading '%1' failed.").arg(filename));    
    return false;
  }
}

bool KSix::saveGame(const QString &filename)
{
  ofstream os(filename.toLatin1());
  (*_match).game().save(os);
  if(!os.fail()) {
    setFilename(filename);
    (*_match).setChanged(false);
    (*_match).setBranched(false);
    return true;
  } else {
    QMessageBox::critical(this,tr("error"), tr("Saving '%1' failed.").arg(filename));
    return false;
  }
}

bool KSix::importPBEMGame(istream &is, const QString &filename)
{
  HexGame g;
  g.importPBEMGame(is);
  if(!is.fail() && 4 <= g.board().xs() && g.board().xs() <= 15 &&
     g.board().xs() == g.board().ys()) {
    setFilename("");
    // sync actions with reality
    int bs = g.board().xs();
    _boardSizeAction->setCurrentItem(bs - 4);
    _boardSize = bs;
    _toggleSwapAction->setChecked(g.initialState().swappable());
    // create new match
    newMatch(g);
    return true;
  } else {
	  QMessageBox::critical(this,tr("error"), tr("Importing '%1' failed.").arg(filename));
    return false;
  }
}

bool KSix::importPBEMGame(const QString &filename)
{
  ifstream is(filename.toLatin1());
  return importPBEMGame(is, filename);
}

void KSix::fileOpen()
{
  const QString q =
    tr("This game has not been saved. Continue loading?");
  QString filename = QFileDialog::getOpenFileName(this, tr("Open Game"),".", "*.six\n*"/*,*/
                                                  /*0,*/ );
  if(!filename.isEmpty()) {
    if(verifyUnsavedGame(q)) {
      loadGame(filename);
    }
  }
}

void KSix::fileSave()
{
  if(_filename.isEmpty()) {
    fileSaveAs();
  } else {
    if((*_match).game().isBranched()) {
      QString w = tr("This game has been branched by moving backwards\n"
                       "in move history and starting play from that position.\n"
                       "Saving will overwrite the original game. Continue?");
      if(QMessageBox::warning(this,tr("warning"), w,QMessageBox::Yes|QMessageBox::No) != QMessageBox::Yes)
        return;
    }
    saveGame(_filename);
  }
}

void KSix::fileSaveAs()
{
  QString filename = QFileDialog::getSaveFileName(this, tr("Save Game"),".", "*.six\n*"/*,0,*/ );
  if(!filename.isEmpty()) {
    if(filename.right(4) != ".six") {
      filename.append(".six");
    }
    FILE *fp;
    if((fp = fopen(filename.toLatin1(), "r")) != NULL) {
      fclose(fp);
      if(QMessageBox::warning(this,tr("Save"),
                                   tr("File '%1' exists. Overwrite?")
                                   .arg(filename),QMessageBox::Yes|QMessageBox::No)
          != QMessageBox::Yes)
        return;
    }
    saveGame(filename);
  }
}

void KSix::fileImportPBEMGame()
{
  const QString q =
    tr("This game has not been saved. Continue importing?");
  QString filename =
    QFileDialog::getOpenFileName(this, tr("Import PBEM Game"),".", "*"/*, 0,*/
                                 /*tr("Import PBEM Game")*/);
  if(!filename.isEmpty()) {
    if(verifyUnsavedGame(q)){
      importPBEMGame(filename);
    }
  }
}

void KSix::filePrint()
{
  // this slot is called whenever the File->Print menu is selected,
  // the Print shortcut is pressed (usually CTRL+P) or the Print toolbar
  // button is clicked
//   if(!_printer) 
//     _printer = new KPrinter;
//   if(_printer->setup(this)) {
//     QPainter p;
//     p.begin(_printer);
// 
//     // we let our view do the actual printing
//     QPaintDeviceMetrics metrics(_printer);
//     _hexWidget->print(&p, QColor("black"), metrics.width(), metrics.height());
// 
//     // and send the result to the printer
//     p.end();
//   }
}


void KSix::showRule()
{
	QString rule = tr("Each player has an allocated color, conventionally Red and Blue or White and Black."
		" Players take turns placing a stone of their color on a single cell within the overall playing board."
		" The goal for each player is to form a connected path of their own stones linking the opposing sides of the board marked by their colors, before their opponent connects his or her sides in a similar fashion."
		" The first player to complete his or her connection wins the game. The four corner hexagons each belong to both adjacent sides."
		"Since the first player to move in Hex has a distinct advantage, the pie rule is generally implemented for fairness."
		"This rule allows the second player to choose whether to switch positions with the first player after the first player makes the first move."
	"more about: https://en.wikipedia.org/wiki/Hex_(board_game)");
	if (_showAboutAction->isChecked())
	{
		_showAboutAction->setChecked(!_showRuleAction->isChecked());
	}

	_helpContentLabel->setVisible(_showRuleAction->isChecked());
	_helpImageLabel->setVisible(_showRuleAction->isChecked());

	if (_showRuleAction->isChecked())
	{
		_helpContentLabel->setText(rule);

		_helpImageLabel->setPixmap(QPixmap(":/File_Hex-board-11x11.jpg"));
	}
}


void KSix::showAbout()
{
	QString rule =tr("Hex is a strategy board game for two players played on a hexagonal grid, theoretically of any size and several possible shapes, but traditionally as an 11x11 rhombus. Players alternate placing markers or stones (Go stones make ideal playing pieces) on unoccupied spaces in an attempt to link their opposite sides of the board in an unbroken chain. One player must win; there are no draws. The game has deep strategy, sharp tactics and a profound mathematical underpinning related to the Brouwer fixed-point theorem. It was invented in the 1940s independently by two mathematicians, Piet Hein and John Nash. The game was first marketed as a board game in Denmark under the name Con-tac-tix, and Parker Brothers marketed a version of it in 1952 called Hex; they are no longer in production. Hex can also be played with paper and pencil on hexagonally ruled graph paper."
		"Hex - related research is current in the areas of topology, graph and matroid theory, combinatorics, game theory and computer heuristics.");
	if (_showRuleAction->isChecked())
	{
		_showRuleAction->setChecked(!_showAboutAction->isChecked());
	}
	

	_helpContentLabel->setVisible(_showAboutAction->isChecked());
	_helpImageLabel->setVisible(_showAboutAction->isChecked());

	if (_showAboutAction->isChecked())
	{
		_helpContentLabel->setText(rule);
		_helpImageLabel->setPixmap(QPixmap(":/220px-Hexposition02.jpg"));
	}
}

bool KSix::queryClose()
{
  const QString q =
    tr("This game has not been saved. Quit anyway?");
  if(!verifyUnsavedGame(q)) {
    return false;
  } else {
    stopMatch();
    return true;
  }
}

void KSix::showToolbar()
{
//   if(_toolbarAction->isChecked())
//     toolBar()->show();
//   else
//     toolBar()->hide();
}

void KSix::showStatusbar()
{
  if(_statusbarAction->isChecked())
    statusBar()->show();
  else
    statusBar()->hide();
}

void KSix::configureKeys()
{
  //KKeyDialog::configureKeys(actionCollection(), "sixui.rc");
}

void KSix::configureToolbars()
{
  // use the standard toolbar editor
//   KEditToolbar dlg(actionCollection());
//   if(dlg.exec()) {
//     // recreate our GUI
//     createGUI();
//   } 
}

void KSix::updateBoard()
{
  if(!_match.null()) {
    HexBoard b((*_match).game().board());
    Grouping bg;
    bg.init(b, HEX_MARK_VERT);
    Grouping wg;
    wg.init(b, HEX_MARK_HORI);
    HexBoard deadVert(b.xs(), b.ys());
    HexBoard deadHori(b.xs(), b.ys());
    HexBoard uselessVert(b.xs(), b.ys());
    HexBoard uselessHori(b.xs(), b.ys());
#if 0
    for(HexField f = HexBoard::FIRST_NORMAL_FIELD; f < b.size(); f++) {
      if(bg(f).null())
        deadVert.set(f, HEX_MARK_VERT);
      if(bg.uselessFields().has(f))
        uselessVert.set(f, HEX_MARK_VERT);
      if(wg(f).null())
        deadHori.set(f, HEX_MARK_HORI);
      if(wg.uselessFields().has(f))
        uselessHori.set(f, HEX_MARK_HORI);
    }
#endif
    if(isTransverted())
      _hexWidget->setGame((*_match).game().transvert(), deadHori.transvert(),
                          deadVert.transvert(), uselessVert.transvert(),
                          uselessHori.transvert());
    else
      _hexWidget->setGame((*_match).game(), deadVert, deadHori,
                          uselessVert, uselessHori);
    if((*_match).status() != HexMatch::MATCH_OFF)
      flash();
  }
}

void KSix::updateActions()
{
  if(!_match.null()) {
    const HexGame &g = (*_match).game();
    _gamePreviousMoveAction->setEnabled((*_match).canBack());
    _gameNextMoveAction->setEnabled((*_match).canForward());
    _gameFirstMoveAction->setEnabled((*_match).canBack());
    _gameLastMoveAction->setEnabled((*_match).canForward());
    _gameSuspendAction->setEnabled((*_match).status() == HexMatch::MATCH_ON);
    _gameResumeAction->setEnabled((*_match).status() == HexMatch::MATCH_OFF);
    bool e = g.board().isEmpty() && !g.isChanged() && _filename.isEmpty() &&
      (_thinking == HEX_MARK_EMPTY);
    _toggleSwapAction->setEnabled(e);
    _boardSizeAction->setEnabled(e);
    bool m = (*_match).status() != HexMatch::MATCH_FINISHED &&
      ((g.next() == HEX_MARK_VERT && _blackPlayer == PLAYER_HUMAN) ||
       (g.next() == HEX_MARK_HORI && _whitePlayer == PLAYER_HUMAN));
    bool s = m && g.isValidMove(HexMove::createSwap(g.next()));
    _gameMoveAction->setEnabled(m);
    _gameResignAction->setEnabled(m);
    _gameSwapAction->setEnabled(s);
  } else {
    _gamePreviousMoveAction->setEnabled(false);
    _gameNextMoveAction->setEnabled(false);
    _gameFirstMoveAction->setEnabled(false);
    _gameLastMoveAction->setEnabled(false);
    _gameSuspendAction->setEnabled(false);
    _gameResumeAction->setEnabled(false);
    _gameMoveAction->setEnabled(false);
    _gameSwapAction->setEnabled(false);
  }
  if(isTransverted()) {
    _blackPlayerAction->setText(tr("&White Player (swapped)"));
    _whitePlayerAction->setText(tr("&Black Player (swapped)"));
  } else {
    _blackPlayerAction->setText(tr("&Black Player"));
    _whitePlayerAction->setText(tr("&White Player"));
  }
}

void KSix::updateClock()
{
  if(!_match.null()) {
    long bc = (isTransverted() ?
               (*_match).horiClockTotal() : (*_match).vertClockTotal());
    long wc = (isTransverted() ?
               (*_match).vertClockTotal() : (*_match).horiClockTotal());
    _blackClockLabel->setText(secToQString(bc / 1000));
    _whiteClockLabel->setText(secToQString(wc / 1000));
    if((*_match).status() != HexMatch::MATCH_ON) {
      _blackClockMark->setEnabled(false);
      _whiteClockMark->setEnabled(false);
    } else if((*_match).game().next() == transvertMark(HEX_MARK_VERT)) {
      _blackClockMark->setEnabled(true);
      _whiteClockMark->setEnabled(false);
    } else {
      _blackClockMark->setEnabled(false);
      _whiteClockMark->setEnabled(true);
    }
  } else {
    _blackClockLabel->setText(secToQString(0));
    _whiteClockLabel->setText(secToQString(0));
    _blackClockMark->setEnabled(false);
    _whiteClockMark->setEnabled(false);
  }
}

void KSix::updateCaption()
{
  QString caption;
  if(!_match.null()) {
    caption = _filename;
    if((*_match).game().isBranched())
      caption += tr("(**)");
    else if((*_match).game().isChanged())
      caption += tr("(*)");
  }
  setWindowTitle(caption);
}

void KSix::updateMessage()
{
  if(!_match.null()) {
    QString statusMessage;
    if((*_match).status() == HexMatch::MATCH_FINISHED) {
      if((*_match).game().winner() == transvertMark(HEX_MARK_VERT)) {
        statusMessage = tr("Black has won.");
      } else {
        statusMessage = tr("White has won.");
      }
    } else {
      if((*_match).game().next() == transvertMark(HEX_MARK_VERT)) {
        statusMessage = tr("Black's turn.");
      } else {
        statusMessage = tr("White's turn.");
      }
    }

    HexGame::ReverseIterator rend = (*_match).game().rend();
    HexGame::ReverseIterator last = (*_match).game().rbegin();
    HexGame::ReverseIterator lastButOne = (*_match).game().rbegin();
    if(lastButOne != rend)
      lastButOne++;

    int n = (*_match).game().initialState().board().nMark();
    QString m0;
    if(last != rend) {
      const HexMove &m = (*last).move();
      m0 = moveToString(m, n + rend - last, (*_match).game().board());
    }
    QString m1;
    if(lastButOne != rend) {
      const HexMove &m = (*lastButOne).move();
      m1 = moveToString(m, n + rend - lastButOne, (*_match).game().board());
    }

    QString s;
    if(!m1.isEmpty() && !m0.isEmpty()) {
      s = QString("%1  %2").arg(m1).arg(m0);
    } else if(!m0.isEmpty()) {
      s = m0;
    }
    QString message("%1   %2");
    _messageLabel->setText(message.arg(s).arg(statusMessage));
  }
}

void KSix::updateThinking()
{
  if(_showThinkingAction->isChecked() && _thinking != HEX_MARK_EMPTY &&
     !_stoppingMatch && !_killingBlackPlayer && !_killingWhitePlayer) {
    HexMove newCandidateMove;
    if((*_match).status() == HexMatch::MATCH_OFF) {
      _hexWidget->stopFlash();
    } else {
      if(onTurn() == HEX_MARK_VERT)
        newCandidateMove = ((SixPlayer *)&*_black)->candidateMove();
      else
        newCandidateMove = ((SixPlayer *)&*_white)->candidateMove();
      if(!_hexWidget->isFlashing() || _candidateMove != newCandidateMove) {
        _candidateMove = newCandidateMove;
        vector<int> f;
        if(_candidateMove.isNormal()) {
          f.push_back(transvertField(_candidateMove.field()) -
                      HexBoard::FIRST_NORMAL_FIELD);
          _hexWidget->flashHexagons(f, 800, 1000000);
        }
      }
    }
  }
}

void KSix::turn()
{
  assert(!_stoppingMatch || _thinking != HEX_MARK_EMPTY);
  if(!_stoppingMatch && _thinking == HEX_MARK_EMPTY) {
    while(!_match.null() && (*_match).status() == HexMatch::MATCH_ON &&
          !_stoppingMatch && _thinking == HEX_MARK_EMPTY &&
          ((onTurn() == HEX_MARK_VERT && _blackPlayer != PLAYER_HUMAN) ||
           (onTurn() == HEX_MARK_HORI && _whitePlayer != PLAYER_HUMAN))) {
      _thinking = onTurn();
      _hexWidget->setCursor(Qt::WaitCursor);
      _candidateMove = HexMove();
      (*_match).doSome();
      _thinking = HEX_MARK_EMPTY;
      _hexWidget->setCursor(Qt::ArrowCursor);
      _candidateMove = HexMove();
      if(_killingBlackPlayer || _killingWhitePlayer) {
        _oldBlack = 0;
        _oldWhite = 0;
        _killingBlackPlayer = false;
        _killingWhitePlayer = false;
        _hexWidget->stopFlash();
      }
    }
    if(_stoppingMatch) {
      _oldMatch = 0;
      _stoppingMatch = false;
      _hexWidget->stopFlash();
    }
	//_hexWidget->update();
	//_hexWidget->repaint();
    updateActions();
    updateMessage();
    updateClock();
  }
}


//

void KSix::gameNew()
{
#if !defined(Q_OS_ANDROID)
  const QString q =
    tr("This game has not been saved. Start a new one?");

  if(verifyUnsavedGame(q))
#endif
    newMatch();
}

void KSix::gamePreviousMove()
{
  if(!_match.null() && (*_match).canBack() && !_stoppingMatch) {
    _stoppingMatch = killMatch();
    (*_match).back();
  }
}

void KSix::gameNextMove()
{
  if(!_match.null() && (*_match).canForward() && !_stoppingMatch) {
    _stoppingMatch = killMatch();
    (*_match).forward();
  }
}

void KSix::gameFirstMove()
{
  if(!_match.null() && (*_match).canBack() && !_stoppingMatch) {
    _stoppingMatch = killMatch();
    (*_match).backAll();
  }
}

void KSix::gameLastMove()
{
  if(!_match.null() && (*_match).canForward() && !_stoppingMatch) {
    _stoppingMatch = killMatch();
    (*_match).forwardAll();
  }
}

void KSix::gameSuspend()
{
  if(!_match.null() && (*_match).status() == HexMatch::MATCH_ON &&
     !_stoppingMatch) {
    (*_match).off();
  }
}

void KSix::gameResume()
{
  if(!_match.null() && (*_match).status() == HexMatch::MATCH_OFF &&
     !_stoppingMatch) {
    (*_match).on();
    turn();
  }
}

class MoveValidator : public QValidator
{
public:
  MoveValidator(const HexGame &g, QObject *parent)
    : QValidator(parent), _game(g) {};
  QValidator::State validate(QString &input, int &) const
  {
    HexMove m;
    if(input.contains(' '))
      return QValidator::Invalid;
    else {
      return ((_game.parseMove(&m, (const char *)input.toStdString().c_str()) &&
               _game.isValidMove(m)) ?
              QValidator::Acceptable : QValidator::Intermediate);
    }
  };
private:
  const HexGame &_game;
};


void KSix::makeMove(const HexMove &move)
{
  if(!_match.null() && !_stoppingMatch) {
    if((*_match).status() != HexMatch::MATCH_FINISHED) {
      const HexGame &g = (*_match).game();
      assert(g.isValidMove(move));
      if(g.next() == HEX_MARK_VERT && _blackPlayer == PLAYER_HUMAN) {
        ((AsyncPlayer *)(&*_black))->play(move);
        (*_match).doSome();
        turn();
      } else if(g.next() == HEX_MARK_HORI && _whitePlayer == PLAYER_HUMAN) {
        ((AsyncPlayer *)(&*_white))->play(move);
        (*_match).doSome();
        turn();
      }
    }
  }
}

void KSix::gameMove()
{
  if(!_match.null() && !_stoppingMatch) {
    const HexGame &g = (*_match).game();
    const HexGame &tg = (isTransverted() ? g.transvert() : g);
    MoveValidator validator(tg, this);
    bool ok;
    QString result =
      QInputDialog::getText(this,tr("Enter move:"), "",QLineEdit::Normal,"", &ok/*, this, &validator*/);
    if (ok) {
      HexMove move;
      tg.parseMove(&move, (const char *)result.toStdString().c_str());
      tg.printMove(DBG, move); DBG << std::endl;
      if (isTransverted())
        move = tg.transvert(move);
      assert(g.isValidMove(move));
      makeMove(move);
    }
  }
}

void KSix::gameSwap()
{
  if(!_match.null() && !_stoppingMatch) {
    if((*_match).status() != HexMatch::MATCH_FINISHED) {
      const HexGame &g = (*_match).game();
      HexMove move = HexMove::createSwap(g.next());
      makeMove(move);
    }
  }
}

void KSix::gameResign()
{
  if(!_match.null() && !_stoppingMatch) {
    if((*_match).status() != HexMatch::MATCH_FINISHED) {
      const HexGame &g = (*_match).game();
      HexMove move = HexMove::createResign(g.next());
      makeMove(move);
    }
  }
}

void KSix::setBoardSize(int s)
{
  if(_boardSize != s) {
    _boardSize = s;
    newMatch();
  }
}

void KSix::setBoardSizeByMenuItem(int item)
{
  setBoardSize(item + 4);
}

void KSix::toggleSwap()
{
  newMatch();
}

void KSix::toggleResign()
{
  if(!_black.null() && _blackPlayer != PLAYER_HUMAN) {
    ((SixPlayer *)&*_black)->allowResign(_toggleResignAction->isChecked());
  }
  if(!_white.null() && _whitePlayer != PLAYER_HUMAN) {
    ((SixPlayer *)&*_white)->allowResign(_toggleResignAction->isChecked());
  }
}

void KSix::toggleNumbering()
{
  if(_toggleNumberingAction->isChecked()) {
    _hexWidget->setNumbering(true);
  } else {
    _hexWidget->setNumbering(false);
  }
}

void KSix::toggleShowThinking()
{
  updateThinking();
}

void KSix::setSwapStyleByMenuItem(int i)
{
  // prevent warning
  i = 0;
  updateBoard();
  updateActions();
  updateClock();
  updateCaption();
  updateMessage();
  updateThinking();
}

void KSix::setDisplayModeByMenuItem(int i)
{
  if(i == 0)
    _hexWidget->setDisplayMode(KHexWidget::DISPLAY_DIAGONAL);
  else
    _hexWidget->setDisplayMode(KHexWidget::DISPLAY_FLAT);
}

void KSix::clickLeft(HexField f)
{
  if(!_match.null() && !_stoppingMatch) {
    if((*_match).status() != HexMatch::MATCH_FINISHED) {
      const HexGame &g = (*_match).game();
      if(isTransverted())
        f = g.board().transvert(f);
      HexMove move(g.next(), f);
      if(g.next() == HEX_MARK_VERT && _blackPlayer == PLAYER_HUMAN) {
        ((AsyncPlayer *)(&*_black))->play(move);
        (*_match).doSome();
        turn();
      } else if(g.next() == HEX_MARK_HORI && _whitePlayer == PLAYER_HUMAN) {
        ((AsyncPlayer *)(&*_white))->play(move);
        (*_match).doSome();
        turn();
      }
    }
    // silently ignore the click
  }
}

bool KSix::killBlack()
{
  DBG << "Killing black" << std::endl;
  if((_thinking == HEX_MARK_VERT) && !_black.null()) {
    ((SixPlayer *)&*_black)->cancelMove();
    return true;
  }
  return false;
}

bool KSix::killWhite()
{
  DBG << "Killing white" << std::endl;
  if((_thinking == HEX_MARK_HORI) && !_white.null()) {
    ((SixPlayer *)&*_white)->cancelMove();
    return true;
  }
  return false;
}

void KSix::setBlack(PlayerType p)
{
  if(!_killingBlackPlayer && killBlack()) {
    _oldBlack = _black;
    _killingBlackPlayer = true;
  }
  //DBG << "Setting black to " << playerToString(p) << std::endl;
  _blackPlayer = p;
  _black = createPlayer(p);
  _blackPlayerAction->setCurrentItem(p);
  if(!_match.null()) {
    (*_match).setVerticalPlayer(_black);
  }
}

void KSix::setWhite(PlayerType p)
{
  if(!_killingWhitePlayer && killWhite()) {
    _oldWhite = _white;
    _killingWhitePlayer = true;
  }
  //DBG << "Setting white to " << playerToString(p) << std::endl;
  _whitePlayer = p;
  _white = createPlayer(p);
  _whitePlayerAction->setCurrentItem(p);
  if(!_match.null()) {
    (*_match).setHorizontalPlayer(_white);
  }
}

void KSix::setBlackPlayerByMenuItem(int i)
{
  if(_blackPlayer != (PlayerType)i)
    setBlack((PlayerType)i);
  turn();
}

void KSix::setWhitePlayerByMenuItem(int i)
{
  if(_whitePlayer != (PlayerType)i)
    setWhite((PlayerType)i);
  turn();
}

//
//
//

void KSix::setFilename(const QString &filename)
{
  _filename = filename;
  updateCaption();
}

bool KSix::killMatch()
{
  if(_thinking != HEX_MARK_EMPTY) {
    killBlack();
    killWhite();
    return true;
  }
  return false;
}

void KSix::stopMatch()
{
  if(!_match.null() && !_stoppingMatch) {
    // We're not interested in this old match anymore,
    disconnect(&*_match, 0, 0, 0);
    if(killMatch()) {
      // but it must be kept alive until control returns from it.
      _oldMatch = _match;
      _match = 0;
      _stoppingMatch = true;
    }
  }
}

void KSix::newMatch(const HexGame &g)
{
  if(!_stoppingMatch) {
    stopMatch();
    setBlack(_blackPlayer);
    setWhite(_whitePlayer);
    _match = new HexMatch(g, _black, _white);

    updateBoard();
    updateActions();
    updateClock();
    updateCaption();
    updateMessage();
    connect(&*_match, SIGNAL(signalPlayerChange()),
            this, SLOT(updateMessage()));
    connect(&*_match, SIGNAL(signalPlayerChange()),
            this, SLOT(updateThinking()));

    connect(&*_match, SIGNAL(signalPositionChange()),
            this, SLOT(updateBoard()));
    connect(&*_match, SIGNAL(signalPositionChange()),
            this, SLOT(updateActions()));
    connect(&*_match, SIGNAL(signalPositionChange()),
            this, SLOT(updateMessage()));

    connect(&*_match, SIGNAL(signalStatusChange()),
            this, SLOT(updateActions()));
    connect(&*_match, SIGNAL(signalStatusChange()),
            this, SLOT(updateMessage()));
    connect(&*_match, SIGNAL(signalStatusChange()),
            this, SLOT(updateClock()));

    connect(&*_match, SIGNAL(signalClockChange()),
            this, SLOT(updateClock()));

    connect(&*_match, SIGNAL(signalChangedGameStatus()),
            this, SLOT(updateCaption()));
  }
}

void KSix::newMatch()
{
  if(!_stoppingMatch) {
    setFilename("");
    HexGame g(HexBoard(_boardSize, _boardSize), HEX_MARK_VERT,
              _toggleSwapAction->isChecked());
    newMatch(g);
  }
}

bool KSix::isTransverted()
{
  if(!_match.null()) {
    const HexGame &g = (*_match).game();
    return ((_swapStyleAction->currentItem() == 1) &&
            g.initialState().swappable() && !g.swappable());
  } else {
    return false;
  }
}

HexMark KSix::transvertMark(HexMark m)
{
  if(isTransverted())
    return INVERT_HEX_MARK(m);
  else return m;
}

HexField KSix::transvertField(HexField f)
{
  if(isTransverted())
    return (*_match).game().board().transvert(f);
  else return f;
}

void KSix::flash()
{
  if((*_match).status() == HexMatch::MATCH_FINISHED) {
    pair<HexMark, vector<HexField> > w =
      (*_match).game().board().winningPath();
    // is it a normal win (not a resignation or forfeit)?
    if(w.first != HEX_MARK_EMPTY) {
      vector<int> wp;
      for(unsigned i = 0; i < w.second.size(); i++) {
        if(w.second[i] >= HexBoard::FIRST_NORMAL_FIELD) {
          wp.push_back(transvertField(w.second[i]) - 
                       HexBoard::FIRST_NORMAL_FIELD);
        }
      }
      _hexWidget->flashHexagons(wp, 200, 2);
    }
  } else {
    HexGame::ReverseIterator rbegin = (*_match).game().rbegin();
    HexGame::ReverseIterator rend = (*_match).game().rend();
    if(rbegin != rend) {
      vector<HexField> fl;
      const HexMove &m = (*(*_match).game().rbegin()).move();
      const HexBoard &b = (*_match).game().board();
      if(m.isNormal()) {
        fl.push_back(transvertField(m.field()) - HexBoard::FIRST_NORMAL_FIELD);
      } else if(m.isSwap()) {
        for(HexField f = HexBoard::FIRST_NORMAL_FIELD; f < b.size(); f++) {
          if(b.get(f) != HEX_MARK_EMPTY)
            fl.push_back(transvertField(f) - HexBoard::FIRST_NORMAL_FIELD);
        }
      }
      _hexWidget->flashHexagons(fl, 100, 2);
    }
  }
}

HexMark KSix::onTurn()
{
  return (*_match).game().next();
}

void KSix::doSlice()
{
  updateThinking();
  int n = 0;
  do {
    if(n > 0) {
      if(n == 1)
        _hexWidget->stopFlash();
      // the game is suspended, let's wait a 0.1s to reduce CPU load
      //usleep(100000L);
	  QThread::usleep(100000L);
    }
    qApp->processEvents();
    ++n;
  } while(!_match.null() && (*_match).status() == HexMatch::MATCH_OFF &&
          !_stoppingMatch && !_killingBlackPlayer && !_killingWhitePlayer);
}

//#include "ksix.moc"
